package com.lonely.framework.aspectj;

import com.lonely.common.annotation.RedisLock;
import com.lonely.common.exception.RedisLockException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * redis锁切面
 *
 * @author fzzf
 */
@Aspect
@Component
@Slf4j
public class RedisLockAspect {

    @Autowired
    private RedissonClient redissonClient;

    @Value("${redisson.lock.default-wait-time:5000}")
    private long defaultWaitTime;

    @Value("${redisson.lock.default-lease-time:30000}")
    private long defaultLeaseTime;

    private final ExpressionParser parser = new SpelExpressionParser();


    /**
     * 环绕切面
     *
     * @param joinPoint
     * @param redisLock
     * @return
     * @throws Throwable
     */
    @Around("@annotation(redisLock)")
    public Object around(ProceedingJoinPoint joinPoint, RedisLock redisLock) throws Throwable {
        // 解析锁key
        String lockKey = resolveLockKey(joinPoint, redisLock);

        // 获取锁对象
        RLock lock = getLock(lockKey, redisLock.lockType());

        // 处理等待时间和租期时间
        long waitTime = redisLock.waitTime() >= 0 ? redisLock.waitTime() : defaultWaitTime;
        long leaseTime = redisLock.leaseTime() >= 0 ? redisLock.leaseTime() : defaultLeaseTime;
        TimeUnit unit = redisLock.unit();

        boolean locked = false;
        try {
            // 尝试获取锁
            if (waitTime == 0) {
                // 不等待，立即尝试
                locked = lock.tryLock(0, leaseTime, unit);
            } else if (waitTime > 0) {
                // 有限等待
                locked = lock.tryLock(waitTime, leaseTime, unit);
            } else {
                // 阻塞直到获取锁
                lock.lock(leaseTime, unit);
                locked = true;
            }

            if (locked) {
                log.debug("Acquired lock: {}", lockKey);
                return joinPoint.proceed();
            } else {
                log.warn("Failed to acquire lock: {}", lockKey);
                return handleLockFailure(joinPoint, redisLock);
            }
        } finally {
            if (locked && lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.debug("Released lock: {}", lockKey);
            }
        }
    }


    /**
     * 解析锁key
     *
     * @param joinPoint
     * @param lock
     * @return
     */
    private String resolveLockKey(ProceedingJoinPoint joinPoint, RedisLock lock) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        // 如果key为空，使用类名+方法名作为默认key
        String keyExpr = lock.key();
        if (!StringUtils.hasText(keyExpr)) {
            return method.getDeclaringClass().getName() + ":" + method.getName();
        }

        // 解析SpEL表达式
        StandardEvaluationContext context = new StandardEvaluationContext();
        Object[] args = joinPoint.getArgs();
        String[] paramNames = signature.getParameterNames();

        for (int i = 0; i < args.length; i++) {
            context.setVariable(paramNames[i], args[i]);
        }

        Expression expression = parser.parseExpression(keyExpr);
        return expression.getValue(context, String.class);
    }

    /**
     * 获取锁
     *
     * @param lockKey
     * @param lockType
     * @return
     */
    private RLock getLock(String lockKey, RedisLock.LockType lockType) {
        switch (lockType) {
            case FAIR:
                return redissonClient.getFairLock(lockKey);
            case READ:
                RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(lockKey);
                return readWriteLock.readLock();
            case WRITE:
                readWriteLock = redissonClient.getReadWriteLock(lockKey);
                return readWriteLock.writeLock();
            case REENTRANT:
            default:
                return redissonClient.getLock(lockKey);
        }
    }

    /**
     * 根据不同锁策略，返回对应结果
     *
     * @param joinPoint
     * @param lock
     * @return
     * @throws Throwable
     */
    private Object handleLockFailure(ProceedingJoinPoint joinPoint, RedisLock lock) throws Throwable {
        switch (lock.failStrategy()) {
            case THROW_EXCEPTION:
                throw new RedisLockException("获取分布式锁失败");
            case RETURN_NULL:
                return null;
            case IGNORE:
                log.warn("Ignoring lock failure and executing method");
                return joinPoint.proceed();
            default:
                throw new UnsupportedOperationException("Unsupported lock failure strategy");
        }
    }
}